using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using System.Text;

namespace RStein.Posterous.API
{
    internal class PostResponseParser : IResultResponseParser<IEnumerable<IPosterousPost>>
    {
        #region Implementation of IResultResponseParser<out IPosterousPost>

        public IEnumerable<IPosterousPost> GetResults(Stream stream)
        {
            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }

            var streamReader = new StreamReader(stream, Encoding.UTF8);
            XmlReader reader = XmlReader.Create(streamReader);
            XElement root = XElement.Load(reader);
            bool isPostOnlyChilds = root.Elements().All(post => post.Name == GlobalConstants.XML_POST_ELEMENT);
            var result = (from elem in root.Elements(GlobalConstants.XML_POST_ELEMENT)
                          let id = (int) elem.Element(GlobalConstants.XML_ID_ELEMENT)
                          let infoElem = isPostOnlyChilds ? elem : root
                          select new PosterousPost(id, PosterousEntityState.Loaded)
                                     {
                                         Url = elem.Element(GlobalConstants.XML_URL_ELEMENT).Value,
                                         Link = elem.Element(GlobalConstants.XML_LINK_ELEMENT).Value,
                                         Title = elem.Element(GlobalConstants.XML_TITLE_ELEMENT).Value,
                                         Body = elem.Element(GlobalConstants.XML_BODY_ELEMENT).Value,
                                         PostDate =
                                             ((DateTime) elem.Element(GlobalConstants.XML_DATE_ELEMENT)).ToLocalTime(),
                                         Views = (int) elem.Element(GlobalConstants.XML_VIEWS_ELEMENT),
                                         Private =
                                             elem.Element(GlobalConstants.XML_PRIVATE_ELEMENT) != null
                                                 ? (bool) elem.Element(GlobalConstants.XML_PRIVATE_ELEMENT)
                                                 : false,
                                         Tags = (from tag in infoElem.Elements(GlobalConstants.XML_TAG_ELEMENT)
                                                 select (string) tag).ToList(),
                                         Author = new PosterousAuthor
                                                      {
                                                          Name =
                                                              (string) elem.Element(GlobalConstants.XML_AUTHOR_ELEMENT),
                                                          AuthorPictureUrl =
                                                              (string)
                                                              elem.Element(GlobalConstants.XML_AUTHORPICTURE_ELEMENT)
                                                      },
                                         AreCommentsEnabled =
                                             (bool) elem.Element(GlobalConstants.XML_COMMENTSENABLED_ELEMENT),
                                         Media = (from medium in infoElem.Elements(GlobalConstants.XML_MEDIA_ELEMENT)
                                                  let mediumInnerElem =
                                                      medium.Element(GlobalConstants.XML_MEDIUM_ELEMENT) ?? medium
                                                  select new PosterousMedium
                                                             {
                                                                 Type =
                                                                     parseMediumType(
                                                                     (string)
                                                                     medium.Element(GlobalConstants.XML_TYPE_ELEMENT)),
                                                                 Url =
                                                                     (string)
                                                                     mediumInnerElem.Element(
                                                                         GlobalConstants.XML_URL_ELEMENT),
                                                                 FileSize =
                                                                     (int)
                                                                     mediumInnerElem.Element(
                                                                         GlobalConstants.XML_FILESIZE_ELEMENT),
                                                                 ExtendedInfo =
                                                                     parseExtendedInfo(mediumInnerElem, medium)

                                                             }).Cast<IPosterousMedium>().ToList(),
                                         Comments =
                                             (from comment in infoElem.Elements(GlobalConstants.XML_COMMENT_ELEMENT)
                                              select new PosterousComment((int?) null)

                                                         {
                                                             Author = new PosterousAuthor
                                                                          {

                                                                              Name =
                                                                                  (string)
                                                                                  comment.Element(
                                                                                      GlobalConstants.XML_AUTHOR_ELEMENT),
                                                                              AuthorPictureUrl =
                                                                                  (string)
                                                                                  comment.Element(
                                                                                      GlobalConstants.
                                                                                          XML_AUTHORPICTURE_ELEMENT)

                                                                          },

                                                             Body =
                                                                 comment.Element(
                                                                 GlobalConstants.XML_BODY_ELEMENT)
                                                                 .Value,
                                                             CreateDate =
                                                                 ((DateTime)
                                                                  comment.Element(GlobalConstants.XML_DATE_ELEMENT)).ToLocalTime()
                                                                 

                                                                }).Cast<IPosterousComment>().ToList()

                                     }).Cast<IPosterousPost>().ToList();


            return result;
        }




        private IDictionary<string, object> parseExtendedInfo(params XElement[] mediumInnerElem)
        {
            var uniquElems = mediumInnerElem.Distinct();

            var retValue = (from uniquEl in uniquElems
                           from el in uniquEl.Elements()
                            where el.Name != GlobalConstants.XML_MEDIUM_ELEMENT &&
                                  el.Name != GlobalConstants.XML_MEDIA_ELEMENT
                           select new {Key = el.Name.ToString(), 
                                        Value =  (el.HasElements ? (object) parseExtendedInfo(el): el.Value)
                           }).ToDictionary(an => an.Key, an => an.Value);            
                                        

            return retValue;
        }

        private MediumType parseMediumType(string val)
        {
            MediumType retVal;

            try
            {
                retVal = (MediumType) Enum.Parse(typeof(MediumType), val, true);
                
            }
            catch (ArgumentException e)
            {
                Debug.WriteLine(e);
                retVal = MediumType.Unknown;
            }
            return retVal;
            
        }

        #endregion
    }
}